package utils;

import cn.hutool.core.date.DateUtil;
import utils.MyException.HttpRequestException;
import utils.MyException.TestDatasException;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.*;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

public class BaseHttpClient {
    private static MyTestLogToTxt log = new MyTestLogToTxt();
    public static CookieManager cookieManager = new CookieManager();

    private BaseHttpClient() {

    }

    private static class InnerClient {
        private static final BaseHttpClient baseHttpClient = new BaseHttpClient();
    }

    public static BaseHttpClient newInstanceHttpClient() {
        return InnerClient.baseHttpClient;
    }

    private HttpClient clientBuilder() {
        HttpClient.Builder builder = HttpClient.newBuilder();
        CookieHandler.setDefault(cookieManager);
        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
        SSLContext sslContext= null;
        try {
            sslContext = SSLContext.getDefault();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        HttpClient client = builder

                //http 协议版本 1.1 或者 2
                .version(HttpClient.Version.HTTP_2) //.version(HttpClient.Version.HTTP_1_1)

                //连接超时时间，单位为毫秒
                .connectTimeout(Duration.ofMinutes(1))

                //连接完成之后的转发策略
                .followRedirects(HttpClient.Redirect.NEVER) //.followRedirects(HttpClient.Redirect.ALWAYS)
                .sslContext(sslContext)
                //指定线程池
//                .executor(Executors.newFixedThreadPool(3))

                //认证，默认情况下 Authenticator.getDefault() 是 null 值，会报错
                //.authenticator(Authenticator.getDefault())

                //代理地址
                //.proxy(ProxySelector.of(new InetSocketAddress("http://www.baidu.com", 8080)))

                //缓存，默认情况下 CookieHandler.getDefault() 是 null 值，会报错
                .cookieHandler(cookieManager)

                //创建完成
                .build();

        return client;
    }

    public HttpResponse<String> doGet(String hostUrl, String requestData, Map<String, String> headers) throws Exception {
        assert requestData != null;
        return httpRequest(hostUrl, requestData, headers, "GET", true);
    }

    public HttpResponse<String> doGet(String hostUrl, Map<String, Object> requestData, Map<String, String> headers) throws Exception {
        assert requestData != null;
        return httpRequest(hostUrl, requestData, headers, "GET", true);
    }

    public HttpResponse<String> doPost(String hostUrl, String requestData, Map<String, String> headers) throws Exception {
        assert requestData != null;
        return httpRequest(hostUrl, requestData, headers, "POST", true);
    }

    public HttpResponse<String> doPost(String hostUrl, Map<String, Object> requestData, Map<String, String> headers) throws Exception {
        assert requestData != null;
        return httpRequest(hostUrl, requestData, headers, "POST", true);
    }

    /**
     * @param hostUrl     host+url
     * @param requestData 请求参数
     * @param headers     请求头
     * @param method      请求方法
     * @return response
     */
    public HttpResponse<String> httpRequest(String hostUrl, String requestData, Map<String, String> headers, String method, boolean isret) throws Exception {
        HttpRequest.Builder builder = HttpRequest.newBuilder();
        if (headers != null)
            headers.forEach(builder::header);
        HttpRequest httpRequest = builder
                .uri(URI.create(hostUrl))
                .method(method, HttpRequest.BodyPublishers.ofString(requestData))
                .build();

        long startTime = DateUtil.current();
        HttpResponse<String> httpResponse = BaseHttpClient.newInstanceHttpClient()
                .clientBuilder()
                .send(httpRequest, HttpResponse.BodyHandlers.ofString());
        long endTime = DateUtil.current();

        int code = httpResponse.statusCode();

        String object_res = httpResponse.body();
        log.printLogs(Config.TESTNUMBER, hostUrl, requestData, method, code, object_res, String.valueOf(endTime - startTime));

        if (code != 200) {
            System.err.println(object_res);
            throw new HttpRequestException("接口响应异常：响应码=" + code);
        }

        return httpResponse;
    }

    /**
     * @param hostUrl     host+url
     * @param requestData 请求参数
     * @param headers     请求头
     * @param method      请求方法
     * @return response
     */
    public HttpResponse<String> httpRequest(String hostUrl, Map<String, Object> requestData, Map<String, String> headers, String method, boolean isret) throws Exception {
        StringBuilder sb_data = new StringBuilder();
        requestData.forEach((k, v) -> sb_data.append(k).append("=").append(v).append("&"));

        HttpRequest.Builder builder = HttpRequest.newBuilder();
        if (headers != null)
            headers.forEach(builder::header);

        HttpRequest httpRequest = builder
                .uri(URI.create(hostUrl))
                .method(method, HttpRequest.BodyPublishers.ofString(sb_data.toString()))
                .build();

        long startTime = DateUtil.current();
        HttpResponse<String> httpResponse = BaseHttpClient.newInstanceHttpClient()
                .clientBuilder()
                .send(httpRequest, HttpResponse.BodyHandlers.ofString());
        long endTime = DateUtil.current();
        int code = httpResponse.statusCode();

        String object_res = httpResponse.body();
        log.printLogs(Config.TESTNUMBER, hostUrl, sb_data.toString(), method, code, object_res, String.valueOf(endTime - startTime));
        if (code != 200) {
            System.err.println(object_res);
            throw new HttpRequestException("接口响应异常：响应码=" + code);
        }

        return httpResponse;
    }

    /**
     * 给指定域名更新cookie
     *
     * @param uri  域名地址
     * @param list 添加的cook键值对字符串，例如："key=value"
     */
    public void setCookies(String uri, String... list) throws IOException {
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(list));
        CookieStore cookieStore = cookieManager.getCookieStore();

        Stream.of(list).forEach(kv -> {
            String key = kv.split("=")[0];

            List<HttpCookie> cookies = cookieStore.getCookies();

            for (int i = 0; i < cookies.size(); i++) {
                HttpCookie cookie = cookies.get(i);
                if (cookie.getName().equals(key)) {
                    cookieStore.remove(URI.create(uri), cookie);
                }
            }
        });
        cookieManager.put(URI.create(uri), headers);
    }
}
